﻿Ext.data.ScriptTagProxy = Ext.extend(Ext.data.ServerProxy, {
    defaultWriterType: 'base',


    callbackParam: "callback",


    scriptIdPrefix: 'stcScript',


    callbackPrefix: 'stcCallback',


    recordParam: 'records',


    lastRequest: undefined,


    autoAppendParams: true,

    constructor: function () {
        this.addEvents(

            'exception'
        );

        Ext.data.ScriptTagProxy.superclass.constructor.apply(this, arguments);
    },

    // 向服务端发送请求
    doRequest: function (operation, callback, scope) {

        var format = Ext.util.Format.format,
            transId = ++Ext.data.ScriptTagProxy.TRANS_ID,
            scriptId = format("{0}{1}", this.scriptIdPrefix, transId),
            stCallback = format("{0}{1}", this.callbackPrefix, transId);

        var writer = this.getWriter(),
            request = this.buildRequest(operation),

            url = Ext.urlAppend(request.url, format("{0}={1}", this.callbackParam, stCallback)); // 组装回调函数

        // 当前操作是否导致写入
        if (operation.allowWrite()) {
            request = writer.write(request);
        }


        Ext.apply(request, {
            url: url,
            transId: transId,
            scriptId: scriptId,
            stCallback: stCallback
        });

        // this.timeout毫秒后运行this.createTimeoutHandler，来清除jsonp相关数据
        request.timeoutId = Ext.defer(this.createTimeoutHandler, this.timeout, this, [request, operation]);

        // 服务端返回并出发的回调函数
        window[stCallback] = this.createRequestCallback(request, operation, callback, scope);

        // 添加script标签，利用标签对服务端进行jsonp请求
        var script = document.createElement("script");
        script.setAttribute("src", url);
        script.setAttribute("async", true);
        script.setAttribute("type", "text/javascript");
        script.setAttribute("id", scriptId);
        Ext.getHead().appendChild(script);

        operation.setStarted();

        this.lastRequest = request;

        return request;
    },

    // 创建回调函数
    createRequestCallback: function (request, operation, callback, scope) {
        var me = this;

        return function (response) {
            var reader = me.getReader(),
                result = reader.read(response);


            Ext.apply(operation, {
                response: response,
                resultSet: result
            });

            operation.setCompleted();
            operation.setSuccessful();

            // 暴露给外部的回调函数运行
            if (typeof callback == 'function') {
                callback.call(scope || me, operation);
            }

            // 运行清理方法时服务端已返回数据
            me.afterRequest(request, true);
        };
    },

    // 指定时间后对jsonp相关数据进行清理工作
    afterRequest: function () {

        // 移除functionName全局方法
        var cleanup = function (functionName) {
            return function () {
                window[functionName] = undefined;

                try {
                    delete window[functionName];
                } catch (e) { }
            };
        };

        return function (request, isLoaded) {
            Ext.get(request.scriptId).remove(); // 移除jsonp数据所在script标签
            clearTimeout(request.timeoutId);     // 清除请求计时器

            // 服务端所返回的js方法名
            var callbackName = request.stCallback;

            // 如果运行清理方法时服务端已返回数据
            if (isLoaded) {
                cleanup(callbackName)(); // 清除服务端返回的方法的引用
                this.lastRequest.completed = true;
            } else {
                // 否则，在服务端返回时清除方法的引用
                window[callbackName] = cleanup(callbackName);
            }
        };
    } (),


    buildUrl: function (request) {
        var url = Ext.data.ScriptTagProxy.superclass.buildUrl.call(this, request),
            params = Ext.apply({}, request.params),
            filters = params.filters,
            filter, i;

        delete params.filters;

        if (this.autoAppendParams) {
            url = Ext.urlAppend(url, Ext.urlEncode(params));
        }

        if (filters && filters.length) {
            for (i = 0; i < filters.length; i++) {
                filter = filters[i];

                if (filter.value) {
                    url = Ext.urlAppend(url, filter.property + "=" + filter.value);
                }
            }
        }


        var records = request.records;

        if (Ext.isArray(records) && records.length > 0) {
            url = Ext.urlAppend(url, Ext.util.Format.format("{0}={1}", this.recordParam, this.encodeRecords(records)));
        }

        return url;
    },


    destroy: function () {
        this.abort();

        Ext.data.ScriptTagProxy.superclass.destroy.apply(this, arguments);
    },


    isLoading: function () {
        var lastRequest = this.lastRequest;

        return (lastRequest != undefined && !lastRequest.completed);
    },


    abort: function () {
        if (this.isLoading()) {
            this.afterRequest(this.lastRequest);
        }
    },


    encodeRecords: function (records) {
        var encoded = "";

        for (var i = 0, length = records.length; i < length; i++) {
            encoded += Ext.urlEncode(records[i].data);
        }

        return encoded;
    },


    createTimeoutHandler: function (request, operation) {
        this.afterRequest(request, false); // 指定时间后对jsonp相关数据进行清理工作

        this.fireEvent('exception', this, request, operation);

        // 如果request带了callback回调函数，运行回调函数
        if (typeof request.callback == 'function') {
            request.callback.call(request.scope || window, null, request.options, false);
        }
    }
});

Ext.data.ScriptTagProxy.TRANS_ID = 1000;

Ext.data.ProxyMgr.registerType('scripttag', Ext.data.ScriptTagProxy);